#!/usr/bin/env python3

# Copyright (c) 2020-2021 Fpemud <fpemud@sina.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import re
import abc
import enum
import strict_hwcfg


class GameStrait:
    pass


class GameStraitForLinux(GameStrait, abc.ABC):

    class RunInfo:

        def __init__(self, executable, *args, cwd=None, extra_environemnt_variables={}):
            self.exectuable = executable
            self.args = args
            self.cwd = cwd
            self.extra_environemnt_variables = extra_environemnt_variables

    @property
    @abc.abstractmethod
    def run_items(self):
        pass

    @abc.abstractmethod
    def _getRunInfo(self, item):
        pass


class GameStraitForScummVM(GameStrait, abc.ABC):

    @abc.abstractmethod
    def get_data_dir(self):
        pass

    @abc.abstractmethod
    def get_language(self):
        pass


class GameStraitForWindows(GameStrait, abc.ABC):

    class WindowsArch(enum.Enum):
        WIN32 = "win32"
        WIN64 = "win64"

    class WindowsVersion(enum.Enum):
        WIN10 = "win10"
        WINXP = "winxp"
        WIN7 = "win7"

    class Registry(abc.ABC):

        @abc.abstractmethod
        def has_key(self, key):
            pass

        @abc.abstractmethod
        def has_value(self, key, value_name, value_type=None):
            pass

        @abc.abstractmethod
        def list_subkeys(self, key, recursive=False):
            pass

        @abc.abstractmethod
        def list_values(self, key):
            pass

        @abc.abstractmethod
        def add_key(self, key):
            pass

        @abc.abstractmethod
        def delete_key(self, key):
            pass

        @abc.abstractmethod
        def get_value(self, key, value_name, value_type=None, fallback_value_type=None, fallback_value=None):
            pass

        @abc.abstractmethod
        def add_value(self, key, value_name, value_type, data):
            pass

        @abc.abstractmethod
        def set_value(self, key, value_name, value_type, data):
            pass

        @abc.abstractmethod
        def delete_value(self, key, value_name):
            pass

        @abc.abstractmethod
        def import_file(self, path, format):
            pass

        @abc.abstractmethod
        def save(self):
            pass

    class RunInfo:

        def __init__(self, command, cwd=None, extra_environemnt_variables={}, custom_dlls=[], cdrom_filepath=None):
            assert re.fullmatch(r"[A-Z]:\\.*", command)
            if cwd is not None:
                assert re.fullmatch(r"[A-Z]:\\.*", cwd)

            self.command = command
            self.cwd = cwd
            self.extra_environemnt_variables = extra_environemnt_variables
            self.custom_dlls = custom_dlls
            self.cdrom_filepath = cdrom_filepath

    @property
    @abc.abstractmethod
    def run_items(self):
        pass

    @abc.abstractmethod
    def _getRunInfo(self, item):
        pass

    def _assertContext(self, context):
        assert hasattr(context, "drive_c_dirpath")
        assert hasattr(context, "registry") or isinstance(context.registry, self.Registry)


class GameStraitForDos(GameStrait, abc.ABC):

    class RunInfo:

        def __init__(self, drive_c_dirpath, command, extra_environemnt_variables=None, cdrom_filepath=None):
            assert re.fullmatch(r"[A-Z]:\\.*", command)

            self.drive_c_dirpath = drive_c_dirpath
            self.command = command
            self.extra_environemnt_variables = extra_environemnt_variables
            self.cdrom_filepath = cdrom_filepath

    @property
    @abc.abstractmethod
    def run_items(self):
        pass

    @abc.abstractmethod
    def _getRunInfo(self, item):
        pass


class GameStraitNeedCacheDir(GameStrait):

    def _assertContext(self, context):
        assert context.cache_dirpath is not None


class GameStraitHasCacheDir(GameStrait):

    def __init__(self, cacheDir):
        self._cacheDir = cacheDir

    def get_cache_dir(self):
        return self._cacheDir


class GameStraitNeedConfigDir(GameStrait):

    def _assertContext(self, context):
        assert context.config_dirpath is not None


class GameStraitHasConfigDir(GameStrait):

    def __init__(self, configDir):
        self._configDir = configDir

    def get_config_dir(self):
        return self._configDir


class GameStraitNeedJoysticks(GameStrait):

    def _assertContext(self, context):
        for c in context.joysticks:
            assert isinstance(c, strict_hwcfg.Component)
            assert c.get_type() in [strict_hwcfg.ComponentType.FLIGHT_STICK, strict_hwcfg.ComponentType.STEERING_WHEEL, strict_hwcfg.ComponentType.GAMEPAD]

        self._context = context

    def _assertHint(self, hint):
        for i in hint.selected_joystick_indices:
            assert 0 <= i < len(self._context.joysticks)
